using System;
using System.Collections.Generic;
using System.Collections ;
using MVCFramework.Models;
using BuildingBasicDomain;
using System.Reflection;
using System.Collections;

namespace MVCFramework.View
{
 
    public enum SNAP_TO_POINTS { middle, intersection }
       

    /// <summary>
    /// SnapManager is Singleton .Accected via Instance .
    /// ContainsGroup an enumarated (in a Dictionary) 
    /// reafecrance to all Snap Points currently activated 
    /// </summary>

    public class SnapManager:IEnumerable
    {
        # region " Fields "

        int counter;

        private Stack <int> freeIds = new Stack<int>();

        IObjectAditionStrategy additionStrategy;
        public IObjectAditionStrategy AdditionStrategy
        {
            set { additionStrategy = value; DoInitialize(); }
        }

        private SNAP_TO_POINTS snapTo;
        public SNAP_TO_POINTS SnapTo
        {
            set
            {
                snapTo = value;
            }

            get
            {
                return snapTo;
            }
        }

        private Dictionary<int, SnapPoint> _snapTable;
        public  Dictionary<int, SnapPoint> SnapTable
        {
            get
            {
                return _snapTable;
            }
        }

        private static SnapManager instance;
        public static SnapManager Instance
        {
            get
            {
                if (instance == null)
                    instance = new SnapManager();
                return instance;

            }
        }

        # endregion 

        public ActionFunction SnapDeepAddition;
        public ActionFunction SnapDeepRemoval;

        private SnapManager()
        {
            _snapTable = new Dictionary<int , SnapPoint>();
            snapTo = SNAP_TO_POINTS.middle;
           // snapFilterBsedToType = new Predicate<SnapPoint>();
            SnapPointTypesActive = new List<Type>();
            SnapPointTypesActive.Add(typeof(MidleSnapPoint));
            
        }

        public void ActivateSnapType(Type typeofSnap)
        {
            if (!SnapPointTypesActive.Contains(typeofSnap))
            {
                SnapPointTypesActive.Add(typeofSnap);
            }
        }
        public void DeActivateSnapType(Type typeofSnap)
        {
            if (SnapPointTypesActive.Contains(typeofSnap))
            {
                SnapPointTypesActive.Remove(typeofSnap);
            }
        }

        private void DoInitialize()
        {
            EventHandler<DomainCollectionChangedEventArgs<object>> CollectionAddhandler = GetNewEventHandler(additionStrategy.AddObject, additionStrategy.RemoveObject);
            SnapDeepAddition = GetDeepCheckingFunction(SnapDeepAddition, additionStrategy.AddObject, RegisterCollectionEventHandler);

            EventHandler<DomainCollectionChangedEventArgs<object>> CollectionRemovehandler = GetNewEventHandler(additionStrategy.AddObject, additionStrategy.RemoveObject);
            SnapDeepRemoval = GetDeepCheckingFunction(SnapDeepRemoval, additionStrategy.RemoveObject, UnRegisterCollectionEventHandler);

        }

        public bool AddSnapPoint (SnapPoint point)
        {

            if (!_snapTable.ContainsValue(point))
            {
                if (freeIds.Count == 0)
                {
                    counter++;
                    _snapTable.Add(counter, point);

                }
                else
                    _snapTable.Add( freeIds.Pop(), point);
                                    Console.WriteLine("Adding New SnapPoint" + point);
                return true;
            }
            return false;
        }
        public bool RemoveSnapPoint(int point)
        {
            if (_snapTable.ContainsKey (point))
            {
                _snapTable.Remove ( point);
                freeIds.Push (point);
                return true;
            }
            return false;
        }

        public void clear()
        {
            _snapTable.Clear();
        }

        public void AddObject(object sender, ModelModificationEventArgs Args)
        {
            if (Args.Changetype == ModelChangeType.Added)
            {
                object modified = Args.Modified;

                if (!modified.GetType().IsDefined(typeof(SnapContainmetAttribute), false)) return;
                SnapDeepAddition(modified);
                additionStrategy.AddObject(modified);

            }
        }

        public void RemoveObject(object sender, ModelModificationEventArgs Args)
        {
            if (Args.Changetype == ModelChangeType.Removed)
            {
                object modified = Args.Modified;

                if (!modified.GetType().IsDefined(typeof(SnapContainmetAttribute), false)) return;
                SnapDeepRemoval(modified);
                additionStrategy.RemoveObject(Args.Modified);
            }

        }

        # region "Reflection ,Closures ,Anonymous  Block DANGER"

        [Obsolete ("Use Closure Instead" ,true )]
        private void CheckSnapDeepAddition(object _object)
        {
            if (_object == null) return;

            if (_object.GetType().IsDefined(typeof(SnapContainmetAttribute), false))
            {
                object[] atrribute = _object.GetType().GetCustomAttributes(typeof(SnapContainmetAttribute), false);
                // bool inheritance = (atrribute[0] as SnapContainmetAttribute).CheckDeepContainment;
                //  if (inheritance == false) return;

                PropertyInfo[] properties = _object.GetType().GetProperties();
                foreach (PropertyInfo property in properties)
                {
                    if (property.IsDefined(typeof(SnapContainmetAttribute), true))
                    {
                        MethodInfo getinfo = property.GetGetMethod(true);
                        object[] aa = new object[0];
                        object _returnObject = getinfo.Invoke(_object, aa);
                        if (_returnObject is IList)
                        {
                            foreach (object data in (_returnObject as IList))
                                additionStrategy.AddObject(data);
                            if (_returnObject is IDomainCollection)
                                RegisterCollectionEventHandler(_returnObject as IDomainCollection);
                        }
                        else if (_returnObject is IModelData)
                        {
                            additionStrategy.AddObject(_returnObject);
                            CheckSnapDeepAddition(_returnObject);
                        }
                    }

                }
            }
        }

        private void RegisterCollectionEventHandler(object  collection)
        {
            if (!(collection is IDomainCollection)) return;
            EventHandler<DomainCollectionChangedEventArgs<object>> handler = GetNewEventHandler(additionStrategy.AddObject, additionStrategy.RemoveObject);
            (collection as IDomainCollection ).OnChange += handler;
            // new EventHandler<DomainCollectionChangedEventArgs<object>>(SnapManagerConatiner_OnChange);
        }

        private void UnRegisterCollectionEventHandler(object collection)
        {
            if (!(collection is IDomainCollection)) return;
          
        }

        public delegate void ActionFunction(object _object);

        # region "Generating Functions"

        public static EventHandler<DomainCollectionChangedEventArgs<object>> GetNewEventHandler(ActionFunction AdditionFunction, ActionFunction RemovalFunction)
        {
            return delegate(object sender, DomainCollectionChangedEventArgs<object> Args)
            {
                if (Args.ChangeType == ChangeType.Removed)
                    RemovalFunction(Args.ChangedItem);
                if (Args.ChangeType == ChangeType.Added)
                    AdditionFunction(Args.ChangedItem);
                if (Args.ChangeType == ChangeType.Cleared)
                    foreach (object _object in Args.items)
                        RemovalFunction(_object);
                if (Args.ChangeType == ChangeType.Replaced)
                { RemovalFunction(Args.ChangedItem); AdditionFunction(Args.ReplacedWith); }

            };
        }


        public static ActionFunction GetDeepCheckingFunction(ActionFunction recurseFunction, ActionFunction FunctionType , ActionFunction EventRegistrationFunction)
        {
            return delegate(object _object)
            {
                if (_object == null) return;

                if (_object.GetType().IsDefined(typeof(SnapContainmetAttribute), false))
                {
                    object[] atrribute = _object.GetType().GetCustomAttributes(typeof(SnapContainmetAttribute), false);
                    // bool inheritance = (atrribute[0] as SnapContainmetAttribute).CheckDeepContainment;
                    //  if (inheritance == false) return;

                    PropertyInfo[] properties = _object.GetType().GetProperties();
                    foreach (PropertyInfo property in properties)
                    {
                        if (property.IsDefined(typeof(SnapContainmetAttribute), true))
                        {
                            MethodInfo getinfo = property.GetGetMethod(true);
                            object[] aa = new object[0];
                            object _returnObject = getinfo.Invoke(_object, aa);
                            if (_returnObject is IList)
                            {
                                foreach (object data in (_returnObject as IList))
                                    FunctionType(data);
                                if (_returnObject is IDomainCollection)
                                    EventRegistrationFunction(_returnObject as IDomainCollection);
                            }
                            else
                            {
                                FunctionType(_returnObject);
                                recurseFunction(_returnObject);
                            }
                        }

                    }
                }
            };
        }
        #endregion
        #endregion

        public void BindManagerToModel(IModel _construction)
        {
            _construction.OnAddElement += new EventHandler<ModelModificationEventArgs>(AddObject);
            _construction .OnRemoveElement += new EventHandler <ModelModificationEventArgs>(RemoveObject);
            additionStrategy.FillManager(_construction);
           // foreach (SnapPoint point in CreatePoints(100, 100, 100, 10, 10, 10))
             //   AddSnapPoint(point);
            CreatePoints(500, 500, 500, 10, 10, 10);
        }

        private SnapPoint[, ,] CreatePoints(double Distance1, double Distance2, double DistanceZ, int Lines1, int Lines2, int Lines3)
        {
          //  throw new Exception(" this must not be used ");
            SnapPoint[, ,] gridPoints = new SnapPoint[Lines1, Lines2, Lines3];


            for (int k = 0; k < Lines3; k++)
            {
                for (int i = 0; i < Lines1; i++)
                {
                    for (int j = 0; j < Lines2; j++)
                    {

                        gridPoints[i, j, k] = new GridSnapPoint (Distance1 * i, Distance2 * j, DistanceZ * k);
                        AddSnapPoint(gridPoints[i, j, k]);
                    }
                }

            }
            return gridPoints;

        }

        #region IEnumerable Members

        public IEnumerator GetEnumerator()
        {
            return _snapTable.Values.GetEnumerator(); 
        }

        #endregion

        public static IList<Type> SnapPointTypesActive;

        public static Predicate<SnapPoint> snapFilterBasedToType =
            delegate(SnapPoint currentPoint)
            {
                if (SnapPointTypesActive.Contains(currentPoint.GetType()))
                    return true;
                return false;
            };


        public static Predicate<SnapPoint> snapFilterBasedToTypeAndRelated =
            delegate(SnapPoint currentPoint)
            {
                    return true;
            };


        public IEnumerable<KeyValuePair<int, SnapPoint>> GetSnapPoints(Predicate<SnapPoint> snapPointFilter)
        {
            foreach (KeyValuePair<int, SnapPoint> pair in this._snapTable )
                if (snapPointFilter(pair.Value))
                    yield return pair;
        }
    }

    [Serializable]
    public class IntersectionArgs : EventArgs
    {
        public ILine[] intersected_lines;
        public IntersectionArgs(ILine line1, ILine line2)
        {
            intersected_lines = new ILine[2];
            intersected_lines[0] = line1;
            intersected_lines[1] = line2;

        }

    }

    public interface IObjectAditionStrategy
    {
        void AddObject(object sender);
        void RemoveObject(object sender);
        void FillManager(IModel model);
    }
}
